package org.aerie.forest.core.frame.rebar.entity.processer.configuration;

import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;

import org.aerie.forest.core.brick.exception.ExceptionPack;
import org.aerie.forest.core.brick.exception.custom.ConfigLoadFailedException;
import org.aerie.forest.core.brick.exception.custom.ContextCheckException;
import org.aerie.forest.core.brick.exception.custom.ForestException;
import org.aerie.forest.core.brick.exception.custom.IncorrectPathException;
import org.aerie.forest.core.frame.GlobalPath;
import org.aerie.forest.core.frame.init.ForestFrameContext;
import org.aerie.forest.core.frame.rebar.entity.processer.AbstractForestProcessor;
import org.aerie.forest.core.frame.rebar.entity.processer.ProcessPack;
import org.aerie.forest.core.frame.rebar.entity.processer.configuration.context.ConfigFilePath;
import org.aerie.forest.core.frame.rebar.entity.processer.configuration.context.ForestContext;
import org.yaml.snakeyaml.Yaml;

/**
 * @description forest配置处理器
 *
 * @author quark
 * @organization aerie
 * @date 2020年2月28日 上午11:26:48
 * @version 1.2.0
 */
public final class ConfigProcessor extends AbstractForestProcessor<ProcessPack, Void> {

	/**
	 * @description 单例对象
	 *
	 * @author quark
	 * @organization aerie
	 * @date 2020-8-13 23:53:24
	 * @version 2.0.1
	 */
	private static final class ForestInstance {
		/**
		 * @description 单例对象
		 * 
		 * @author quark
		 * @date 2020-7-9 21:16:25
		 * @version 2.0.1
		 */
		private static final ConfigProcessor CONFIG_PROCESSOR = new ConfigProcessor();
	}

	/**
	 * @description 自定义字符串
	 * 
	 * @author quark
	 * @date 2020-8-18 14:54:00
	 * @version 2.0.1
	 */
	private static final String CUSTOM_STRING = "】类的属性：【";

	/**
	 * Construction
	 *
	 * @author quark
	 * @date 2020年6月17日 下午2:58:46
	 * @version 1.2.2
	 */
	private ConfigProcessor() {
		super("配置容器处理器");
	}

	/**
	 * @description 获得forest配置处理器单例
	 * @return forest配置处理器
	 *
	 * @author quark
	 * @date 2020年6月17日 下午3:00:36
	 * @version 1.2.2
	 */
	static ConfigProcessor getInstance() {
		return ForestInstance.CONFIG_PROCESSOR;
	}

	/**
	 * @description 加载配置文件【指定的配置文件，对接ForestContext配置文件】
	 * @throws Exception 加载异常
	 *
	 * @author quark
	 * @date 2020年6月10日 下午6:01:45
	 * @version 1.2.2
	 */
	public <T extends ForestContext> T loadYamlConfig(Class<T> contextClass) throws Exception {
		String filePath;
		// 校验上下文配置类
		try {
			filePath = getConfigFilePath(contextClass).configFilePath();
			LOGGER.debug("开始加载配置文件：【{}】", filePath);
			// 校验
			checkContext(contextClass, new ArrayList<>());
		} catch (Exception e) {
			throw new ExceptionPack("上下文配置类【" + contextClass.getName() + "】校验不通过", e);
		}
		// 获得配置文件流
		try (InputStream resourceAsStream = ForestFrameContext.INSTANCE.forestClassLoader
				.getResourceAsStream(filePath)) {
			if (resourceAsStream == null)
				throw new ConfigLoadFailedException("配置文件：【" + filePath + "】不存在");
			return new Yaml().loadAs(resourceAsStream, contextClass);
		} catch (Exception e) {
			throw new ExceptionPack("加载配置文件失败", e);
		}
	}

	/**
	 * @description 通过注解获得环境配置文件路径
	 * @param contextClass 配置文件类
	 * @return 环境配置文件的路径
	 * @throws Exception 解析异常
	 *
	 * @author quark
	 * @date 2020-8-20 14:16:19
	 * @version 2.0.1
	 */
	private ConfigFilePath getConfigFilePath(Class<? extends ForestContext> contextClass) throws Exception {
		return Optional.ofNullable(contextClass.getAnnotation(ConfigFilePath.class))
				.orElseThrow(() -> new ContextCheckException(
						contextClass.getName() + ":缺少" + ConfigFilePath.class.getSimpleName() + "注解"));
	}

	/**
	 * @description 校验上下文的合法性【是否存在空指针】内部方法，形参不做类的限制
	 * @throws Exception 检测出的异常
	 *
	 * @author quark
	 * @date 2020年6月11日 下午5:45:02
	 * @version 1.2.2
	 */
	private void checkContext(Class<?> zClass, List<Class<?>> zclasses) throws Exception {
		LOGGER.debug("开始检测上下文：【{}】", zClass.getName());
		// 待校验
		List<Class<?>> zclassVerifys = zclasses;
		zclassVerifys.add(zClass);
		// 获得类的所有字段
		Field[] fields = zClass.getDeclaredFields();
		for (Field field : fields) {
			// 校验的上下文中的类是否是合法
			Class<?> type = field.getType();
			String typeName = type.getName();
			// 校验基本类型，数组类型【详见类型枚举】
			if (checkFieldBaseType(type) || checkFieldArrayType(type))
				continue;
			// 字段如果是forest上下文类型则进行递归检测
			if (ForestContext.class.isAssignableFrom(type)) {
				// 禁止配置文件嵌套循环
				for (Class<?> zclassVerify : zclassVerifys) {
					if (zclassVerify.isAssignableFrom(type))
						throw new ContextCheckException(
								"【" + zClass.getName() + CUSTOM_STRING + typeName + "】存在循环递归风险");
				}
				zclassVerifys.add(type);
				checkContext(type, zclassVerifys);
				continue;
			}
			// 禁止集合
			if (List.class.isAssignableFrom(type)) {
				throw new ContextCheckException("【" + zClass.getName() + CUSTOM_STRING + typeName + "】请用数组代替集合");
			}
			throw new ContextCheckException("【" + zClass.getName() + CUSTOM_STRING + typeName + "】不是合法类型");
		}
		LOGGER.debug("上下文：【{}】属性字段校验通过", zClass.getName());
	}

	/**
	 * @description 检测class是否属于上下文基本类型
	 * @param zClass 被检查的类
	 * @return 检查的结果
	 *
	 * @author quark
	 * @date 2020年6月17日 上午9:25:34
	 * @version 1.2.2
	 */
	private boolean checkFieldBaseType(Class<?> zClass) {
		// 校验的上下文中的类是否是合法
		for (ContextAttributeBaseType contextAttributeBaseType : ContextAttributeBaseType.values()) {
			if (zClass.equals(contextAttributeBaseType.getZclass())) {
				LOGGER.trace("类：【{}】符合上下文基本类型", zClass.getName());
				return true;
			}
		}
		LOGGER.trace("类：【{}】不是上下文基本类型", zClass.getName());
		return false;
	}

	/**
	 * @description 检测class是否属于上下文数组类型
	 * @param zClass 被检查的类
	 * @return 检查的结果
	 *
	 * @author quark
	 * @date 2020-8-18 11:19:37
	 * @version 2.0.1
	 */
	private boolean checkFieldArrayType(Class<?> zClass) {
		// 校验的上下文中的类是否是合法
		for (ContextAttributeArrayType contextAttributeArrayType : ContextAttributeArrayType.values()) {
			if (zClass.getName().equals(contextAttributeArrayType.getZclass())) {
				LOGGER.trace("类：【{}】符合上下文数组类型", zClass.getName());
				return true;
			}
		}
		LOGGER.trace("类：【{}】不是上下文数组类型", zClass.getName());
		return false;
	}

	/**
	 * @description 加载yaml配置文件转成map
	 * @return 加载的结果
	 * @throws Exception 加载异常
	 *
	 * @author quark
	 * @date 2020-8-18 16:19:55
	 * @version 2.0.1
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> loadYamlConfig(String filePath) throws Exception {
		try (InputStream resourceAsStream = ForestFrameContext.INSTANCE.forestClassLoader
				.getResourceAsStream(Optional.ofNullable(filePath).filter(p1 -> p1.endsWith(".yml"))
						.orElseThrow(() -> new IncorrectPathException(filePath)))) {
			if (resourceAsStream == null)
				throw new ConfigLoadFailedException("配置文件：【" + filePath + "】不存在");
			return new Yaml().loadAs(resourceAsStream, Map.class);
		} catch (Exception e) {
			throw new ExceptionPack("加载yaml配置文件失败", e);
		}
	}

	/**
	 * @description 根据forest上下文配置类生成yaml配置文件
	 * @param contextClass forest上下文配置类
	 * @throws Exception 生成异常
	 *
	 * @author quark
	 * @date 2020-8-20 14:28:38
	 * @version 2.0.1
	 */
	public void generateYamlFile(Class<? extends ForestContext> contextClass) throws Exception {
		try {
			StringBuilder absolutePath = new StringBuilder(new File("").getAbsolutePath());
			String configFileAbsolutePath = absolutePath.append(GlobalPath.RESOURCE_PATH).append("/")
					.append(getConfigFilePath(contextClass).configFilePath()).toString();
			createFileWithAbsolutePath(configFileAbsolutePath);
			Yaml yaml = new Yaml();
			yaml.dump(contextClass.newInstance(), new FileWriter(configFileAbsolutePath));
		} catch (Exception e) {
			throw new ExceptionPack("根据【" + contextClass.getName() + "】生成yaml文件失败", e);
		}
	}

	/**
	 * @description 绝对路径创建文件
	 * @param fileAbsolutePath 文件的绝对路径
	 * @throws Exception 创建异常
	 *
	 * @author quark
	 * @date 2020-8-20 16:43:13
	 * @version 2.0.1
	 */
	private void createFileWithAbsolutePath(String fileAbsolutePath) throws Exception {
		File contextFile = new File(fileAbsolutePath);
		if (!contextFile.exists()) {
			File filePath = new File(fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf("/")));
			if (!filePath.exists() && !filePath.mkdirs()) {
				throw new ForestException("创建目录失败") {

					/**
					 * @description
					 * 
					 * @author quark
					 * @date 2020-8-20 16:25:52
					 * @version 2.0.1
					 */
					private static final long serialVersionUID = 1L;
				};
			}
			if (!contextFile.createNewFile())
				throw new ForestException("创建配置文件：【" + fileAbsolutePath + "】失败") {

					/**
					 * @description
					 * 
					 * @author quark
					 * @date 2020-8-20 16:24:29
					 * @version 2.0.1
					 */
					private static final long serialVersionUID = 1L;
				};
		}
	}

	/**
	 * @description 加载Properties配置文件
	 * @param filePath 文件路径
	 * @return 加载的结果
	 * @throws Exception 加载异常
	 *
	 * @author quark
	 * @date 2020-8-20 11:18:43
	 * @version 2.0.1
	 */
	public Properties loadPropertiesConfig(String filePath) throws Exception {
		try (InputStream resourceAsStream = ForestFrameContext.INSTANCE.forestClassLoader
				.getResourceAsStream(Optional.ofNullable(filePath).filter(p1 -> p1.endsWith(".properties"))
						.orElseThrow(() -> new IncorrectPathException(filePath)))) {
			if (resourceAsStream == null)
				throw new ConfigLoadFailedException("配置文件：【" + filePath + "】不存在");
			Properties result = new Properties();
			result.load(resourceAsStream);
			return result;
		} catch (Exception e) {
			throw new ExceptionPack("加载Properties配置文件失败", e);
		}
	}

	@Override
	protected Void analysisPack(ProcessPack t) {
		// TODO Auto-generated method stub
		return null;
	}
}
